/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.dotexe.webframework.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.UUID;

import org.json.JSONException;
import org.json.JSONObject;

import com.dotexe.webframework.core.clientactions.BindDataSourceConfiguration;
import com.dotexe.webframework.core.clientactions.ChangeControlText;
import com.dotexe.webframework.core.clientactions.ClientActionConfiguration;
import com.dotexe.webframework.core.clientactions.HideUseCaseConfiguration;
import com.dotexe.webframework.core.clientactions.StartUseCaseConfiguration;
import com.dotexe.webframework.core.clientactions.ResizeWindow;
import com.dotexe.webframework.core.clientactions.ChangeControlVisibility;
import com.dotexe.webframework.core.clientactions.CharacterCounter;
import com.dotexe.webframework.core.clientactions.ChangeControlCollapsible;
import com.dotexe.webframework.core.communication.CallBackResult;
import com.dotexe.webframework.core.datasources.DataSource;
import com.dotexe.webframework.core.datasources.DataSourceCollection;
import com.dotexe.webframework.core.eventargs.RefreshStateArgs;
import com.dotexe.webframework.core.triggers.TriggerCollection;

/**
 *
 * @author jovan.tomasevic
 */
public class ClientApplication {

    private CallBackResult response;
//    protected StartUseCaseConfiguration StartUseCaseConfiguration;
    public String elementID;
    public Boolean isLoaded;
    public ApplicationController controller;
    public ClientApplication owner;
    public TriggerCollection triggers;
    private DataSourceCollection dataSources;
    public ArrayList<ClientActionConfiguration> clientActions;
    public String name;
    public String holderID;
    private StartUseCaseConfiguration config;
    private ArrayList<ClientApplication> childUseCases;

    public ClientApplication() {
        this.isLoaded = false;
        dataSources = new DataSourceCollection();
        triggers = new TriggerCollection();
        clientActions = new ArrayList<ClientActionConfiguration>();
        childUseCases = new ArrayList<ClientApplication>();
    }

    public void start() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, JSONException, NoSuchMethodException, InvocationTargetException, Exception {
        if ( !this.isLoaded ) {
            this.load();
        }
        config = new StartUseCaseConfiguration();
        config.setApplicationName( this.getClass().getSimpleName() );
        config.setElementID( this.elementID );
        //config.setHolderID(app.holderID);

        this.clientActions.add( config );
    }

    public void startIn( String holderID ) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, JSONException, NoSuchMethodException, InvocationTargetException, Exception {
        // ??? jovan.tomasevic
        if ( !this.isLoaded ) {
            this.load();
        }
        config = new StartUseCaseConfiguration();
        config.setApplicationName( this.getClass().getSimpleName() );
        config.setElementID( this.elementID );
        config.setHolderID( holderID );
        config.setOwnerID( this.owner.elementID );

        owner.clientActions.add( config );
        //this.clientActions.add(config);
        this.start();

    }

    public void showChildApp( ClientApplication app, String controlHolder ) throws JSONException {
        config = new StartUseCaseConfiguration();
        config.setApplicationName( app.name );
        config.setModal( true );
        config.setOwnerID( this.elementID );
        config.setElementID( app.elementID );

        config.setHolderID( controlHolder );
        this.clientActions.add( config );
    }

    public void showChildApp( ClientApplication app ) throws JSONException {
        config = new StartUseCaseConfiguration();
        config.setApplicationName( app.name );
        config.setModal( true );
        config.setOwnerID( this.elementID );
        config.setElementID( app.elementID );
        this.clientActions.add( config );
    }

    public <T extends ClientApplication> T createChildApplication( Class<T> c ) throws InstantiationException, IllegalAccessException, JSONException, Exception {
        return this.createChildApplication( c, true );
//        StartUseCaseConfiguration config = new StartUseCaseConfiguration();
//        config.setApplicationName (child.name);
//        config.setModal(true);
//        config.setOwnerID(this.elementID);
//        config.setElementID(child.elementID);
//        this.clientActions.add(config);
        //return child;
    }

    public <T extends ClientApplication> T createChildApplication( Class<T> c, boolean modal ) throws InstantiationException, IllegalAccessException, JSONException, Exception {
        ClientApplication child = getApp( c );
        if ( child == null ) {
            child = c.newInstance();
            child.controller = this.controller;
            controller.apps.add( child );
            this.childUseCases.add( child );
            child.name = c.getSimpleName();
            child.owner = this;
            child.elementID = UUID.randomUUID().toString();
            child.load();
            child.isLoaded = true;

        }
        return (T) child;
    }

    public <T extends ClientApplication> T LoadChildApp( Class<T> appType ) throws InstantiationException, IllegalAccessException, JSONException, Exception {
        ClientApplication app = getApp( appType );
        if ( app == null ) {
            app = appType.newInstance();
            app.controller = this.controller;
            controller.apps.add( app );
            this.childUseCases.add( app );
            app.name = appType.getSimpleName();
            app.owner = this;
            app.elementID = UUID.randomUUID().toString();
            app.load();
        }
        return (T) app;
    }

    /**
     * In this implementation this method is used for datasources and triggers initialization.
     * In next version here should be loading from xml
     */
    public void load() throws Exception {
        this.isLoaded = true;
    }

    public void hide() throws JSONException {
        HideUseCaseConfiguration hide = new HideUseCaseConfiguration();
        //if(this.owner!=null){
        hide.setOwnerID( this.elementID );
        //}
        this.clientActions.add( hide );
    }

    public void prepareResponse() throws JSONException {
        response = new CallBackResult();

        for ( int i = 0; i < this.clientActions.size(); i++ ) {
            ClientActionConfiguration ds = this.clientActions.get( i );
            if ( ds.getOwnerID() == JSONObject.NULL || ds.getOwnerID().isEmpty() ) {
                ds.setOwnerID( this.elementID );
            }
            getResponse().getClientActions().put( ds );
        }
        for ( int i = 0; i < this.getDataSources().size(); i++ ) {
            DataSource ds = this.getDataSources().get( i );
            if ( ds.getSource() != null && ds.BindingSource != null ) {
                BindDataSourceConfiguration bConfig = new BindDataSourceConfiguration( ds.BindingSource );
                bConfig.setOwnerID( this.elementID );
                getResponse().getClientActions().put( bConfig );
                ds.BindingSource = null;
            }
        }
        getResponse().setElementID( this.elementID );
    }

    public void clearRespones() throws JSONException {
        this.response = new CallBackResult();
        this.clientActions = new ArrayList<ClientActionConfiguration>();
        for ( int i = 0; i < this.getDataSources().size(); i++ ) {
            this.dataSources.get( i ).BindingSource = null;
        }
        for ( int i = 0; i < this.getDataSources().size(); i++ ) {
            DataSource ds = this.getDataSources().get( i );
            ds.BindingSource = null;
        }
    }

    public void callMethod( RefreshStateArgs args ) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class c = this.getClass();
        Method m = c.getMethod( args.clientOperationName );
        m.invoke( this );
    }

    void invokeTrigger( RefreshStateArgs args ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
        this.triggers.get( args.clientOperationName ).fire();
    }

    public <T extends ClientApplication> ClientApplication getApp( Class<T> appType ) {
        for ( int i = 0; i < this.childUseCases.size(); i++ ) {
            if ( this.childUseCases.get( i ).getClass().getSimpleName().equals( appType.getSimpleName() ) ) {
                return this.childUseCases.get( i );
            }
        }
        return null;
    }

    /**
     * @return the config
     */
    public StartUseCaseConfiguration getConfig() {
        return config;
    }

    /**
     * @return the dataSources
     */
    public DataSourceCollection getDataSources() {
        return dataSources;
    }

    /**
     * @param dataSources the dataSources to set
     */
    public void setDataSources( DataSourceCollection dataSources ) {
        this.dataSources = dataSources;
    }

    /**
     * @return the response
     */
    public CallBackResult getResponse() {
        return response;
    }

    public void ResizeWindow() throws JSONException{
        ResizeWindow ca = new ResizeWindow();
        ca.setOwnerID(this.elementID);
        this.clientActions.add(ca);
    }

    public void CharacterCounter(String TextFieldName, String LabelName) throws JSONException{
        CharacterCounter ca = new CharacterCounter();
        ca.setOwnerID(this.elementID);
        ca.setLabelName(LabelName);
        ca.setTextField(TextFieldName);
        this.clientActions.add(ca);
    }

     public void ChangeControlText(String control, String text) throws JSONException{
        ChangeControlText cat = new ChangeControlText();
        cat.setOwnerID(this.elementID);
        cat.setText(text);
        cat.setControlName(control);
        this.clientActions.add(cat);
    }

      public void ChangeControlVisibility(String control, boolean visible) throws JSONException{
        ChangeControlVisibility cav = new ChangeControlVisibility();
        cav.setOwnerID(this.elementID);
        cav.setVisible(visible);
        cav.setControlName(control);
        this.clientActions.add(cav);
    }

       public void ChangeControlCollapsible(String control, boolean collapse) throws JSONException{
        ChangeControlCollapsible cac = new ChangeControlCollapsible();
        cac.setOwnerID(this.elementID);
        cac.setCollapse(collapse);
        cac.setControlName(control);
        this.clientActions.add(cac);
    }
}
